﻿ //Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Reflection;
using System.Collections.Concurrent;
using FinPlusBaseCore;
using FinPlusInterfaces;
using FinPlusAssembler;

namespace FinPlusConnService
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
    public class FinPlusServices : IFinPlusServices
    {
        private volatile static ConcurrentDictionary<string, FinPlusService> _services = new ConcurrentDictionary<string, FinPlusService>();
        private string _targetAssemblys, _configFolder;
        private int _asyncWait, _asyncRetry;
        private bool _hasInitialised;
        private Env _env;
    
        //public
        public void GetPods(Env env, string user, string config, string serviceKey, string serviceType, string query, string guid)
        {
            try
            {
                if (!_hasInitialised) 
                    Initialise();

                FinPlusService service;
                if (!_services.ContainsKey(serviceKey))
                    lock (_services)
                        if (!_services.ContainsKey(serviceKey))
                            _services.TryAdd(serviceKey, service = new FinPlusService(env, serviceKey, serviceType, query, _targetAssemblys, _configFolder, _asyncWait, _asyncRetry));
     
                _services.TryGetValue(serviceKey, out service);
      
                if (!service.Started) 
                    service.Start();
                else
                    service.WaitCompleted();

                service.SendResults(guid);
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
                throw new FaultException(e.Message);
            }
        }

        public void SavePod(Env env, string user, string config, string serviceKey, IEnumerable<Bean> value, ChangeType changeType, string guid)
        {
            try
            {
                FinPlusService service;
                if(_services.TryGetValue(serviceKey, out service))
                    service.Service.Adapter.Update(new Pod(value), changeType);
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
                throw new FaultException(e.Message);
            }
        }

        public void AlterPod(Env env, string user, string config, string serviceKey, DateTime timeStamp, string Type, int id, string beanName, object value)
        {
            try
            {
                FinPlusService service;
                if (!_services.TryGetValue(serviceKey, out service)) 
                    return;

                if (!service.Service.Adapter.ContainsKey(id)) 
                    return;

                var bean = service.Service.Adapter[id].Get(beanName);
                if (bean != null && timeStamp > bean.TimeStamp)
                    bean.Update(value, timeStamp, user);
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
                throw new FaultException(e.Message);
            }
        }

        public IEnumerable<IEnumerable<Bean>> Infos()
        {
            var infos = new List<IEnumerable<Bean>>();
            foreach(var service in _services.Values)
                infos.Add(service.Info());

            return infos;
        }

        public bool Stop(string serviceKey)
        {
            try
            {
                FinPlusService service;
                if(!_services.TryGetValue(serviceKey, out service))
                    throw new ServiceConnException(string.Format("missing service key {0}", serviceKey));
                service.Stop();
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(string.Format("failed to stop service {0}", serviceKey), e);
                return false;
            }
            return true;
        }

        public bool Start(string serviceKey)
        {
            try
            {
                FinPlusService service;
                if(!_services.TryGetValue(serviceKey, out service))
                    throw new ServiceConnException(string.Format("missing service key {0}", serviceKey));
                service.Start();
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(string.Format("failed to start service {0}", serviceKey), e);
                return false;
            }
            return true;
        }

        public void Dispose(Env env, string user, string config, string serviceKey, string guid)
        {
            try
            {
                FinPlusService service;
                if (_services.TryGetValue(serviceKey, out service))
                    service.Dispose(guid);
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
            }
        }

        //private
        private void Initialise()
        {
            var path = Assembly.GetExecutingAssembly().ConfigPath();
            var configs = path.ConfigXml(out _env)["configs"];

            _configFolder = configs["configfolder"];
            _targetAssemblys = configs["targetassemblys"];
            _asyncWait = (int)(double.Parse(configs["asyncwait"]) * 1000);
            _asyncRetry = int.Parse(configs["asyncretry"]);
            _hasInitialised = true;
        }
    }
}
